package org.omorphdb.core.index.hash;

import java.util.HashSet;
import java.util.Random;

import org.omorphdb.core.value.ValueType;

public class HashFunctionDemo
{
	static final int ATTEMPTS = Short.MAX_VALUE;
	static Random RANDOM = new Random();

	static byte[] CHAR_ARRAY = new byte[] { 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 'a', 's', 'd',
			'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '`', '1',
			'2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(',
			')', '_', '+', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 'A', 'S', 'D', 'F', 'G', 'H',
			'J', 'K', 'L', ':', '"', '|', '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?' };

	public static void main(String[] args)
	{
		forDouble();
		forLong();
		forString();
	}

	private static void forLong()
	{
		checkLong(new HashFunctionFactory<Long>(ValueType.LONG), ValueType.BYTE);
		checkLong(new HashFunctionFactory<Long>(ValueType.LONG), ValueType.SHORT);
		checkLong(new HashFunctionFactory<Long>(ValueType.LONG), ValueType.INT);
		checkLong(new HashFunctionFactory<Long>(ValueType.LONG), ValueType.LONG);
	}

	private static void forDouble()
	{
		checkDouble(new HashFunctionFactory<Double>(ValueType.DOUBLE), ValueType.BYTE);
		checkDouble(new HashFunctionFactory<Double>(ValueType.DOUBLE), ValueType.SHORT);
		checkDouble(new HashFunctionFactory<Double>(ValueType.DOUBLE), ValueType.INT);
		checkDouble(new HashFunctionFactory<Double>(ValueType.DOUBLE), ValueType.LONG);
	}

	private static void forString()
	{
		checkString(new HashFunctionFactory<String>(ValueType.STRING), ValueType.BYTE);
		checkString(new HashFunctionFactory<String>(ValueType.STRING), ValueType.SHORT);
		checkString(new HashFunctionFactory<String>(ValueType.STRING), ValueType.INT);
		checkString(new HashFunctionFactory<String>(ValueType.STRING), ValueType.LONG);
	}

	private static void checkLong(HashFunctionFactory<Long> factory, ValueType algorithm)
	{
		HashFunction<Long> hash = factory.getHashFunction(algorithm.getByteType());

		System.out.println("checking statistics of random generated long with '" + algorithm.toString()
				+ "' hash algorithm...");
		HashSet<Long> usedSlots = new HashSet<>();
		int collisions = 0;

		for (int i = 0; i < ATTEMPTS; i++)
		{
			long calculated = hash.calculate(RANDOM.nextLong());
			if (usedSlots.contains(calculated)) collisions++;
			usedSlots.add(calculated);
		}

		System.out.println("Used slots: " + usedSlots.size() + " - collisions: " + collisions);
	}

	private static void checkDouble(HashFunctionFactory<Double> factory, ValueType algorithm)
	{
		HashFunction<Double> hash = factory.getHashFunction(algorithm.getByteType());

		System.out.println("checking statistics of random generated double with '" + algorithm.toString()
				+ "' hash algorithm...");
		HashSet<Long> usedSlots = new HashSet<>();
		int collisions = 0;

		for (int i = 0; i < ATTEMPTS; i++)
		{
			long calculated = hash.calculate(RANDOM.nextDouble());
			if (usedSlots.contains(calculated)) collisions++;
			usedSlots.add(calculated);
		}

		System.out.println("Used slots: " + usedSlots.size() + " - collisions: " + collisions);
	}

	private static void checkString(HashFunctionFactory<String> factory, ValueType algorithm)
	{
		HashFunction<String> hash = factory.getHashFunction(algorithm.getByteType());

		System.out.println("checking statistics of random generated strings with '" + algorithm.toString()
				+ "' hash algorithm...");
		HashSet<Long> usedSlots = new HashSet<>();
		int collisions = 0;

		for (int i = 0; i < ATTEMPTS; i++)
		{
			long calculated = hash.calculate(createRandomString());
			if (usedSlots.contains(calculated)) collisions++;
			usedSlots.add(calculated);
		}

		System.out.println("Used slots: " + usedSlots.size() + " - collisions: " + collisions);
	}

	private static String createRandomString()
	{
		int l = RANDOM.nextInt(10000);
		StringBuilder builder = new StringBuilder();

		for (int j = 0; j < l; j++)
			builder.append(CHAR_ARRAY[RANDOM.nextInt(CHAR_ARRAY.length)]);

		return builder.toString();
	}
}
